/**
 * UserServiceImpl.java
 * 
 * Copyright@2015 OVT Inc. All rights reserved. 
 * 
 * May 5, 2015
 */
package com.ovt.doorbell.payment.service;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.ovt.doorbell.payment.common.log.Logger;
import com.ovt.doorbell.payment.common.log.LoggerFactory;
import com.ovt.doorbell.payment.common.utils.DataFormatValidator;
import com.ovt.doorbell.payment.common.utils.EncryptionUtils;
import com.ovt.doorbell.payment.common.utils.StringUtils;
import com.ovt.doorbell.payment.dao.config.AppProperties;
import com.ovt.doorbell.payment.dao.constant.LoggerConstants;
import com.ovt.doorbell.payment.dao.vo.PageInfo;
import com.ovt.doorbell.payment.dao.vo.User;
import com.ovt.doorbell.payment.service.context.SessionContext;
import com.ovt.doorbell.payment.service.exception.InvalidDataInputException;
import com.ovt.doorbell.payment.service.exception.ServiceErrorCode;
import com.ovt.doorbell.payment.service.exception.ServiceException;
import com.ovt.doorbell.payment.service.manager.UserTokenManager;
import com.ovt.doorbell.payment.service.manager.UserManager;

/**
 * UserServiceImpl
 * 
 * @Author zhi.liu
 * @Version 1.0
 * @See
 * @Since [OVT Cloud Platform]/[API] 1.0
 */
@Service
public class UserServiceImpl implements UserService
{
    @Autowired
    private UserTokenManager userAccessTokenManager;

    @Autowired
    private UserManager userManager;

    @Autowired
    private AppProperties appProperties;

    private static final Logger logger = LoggerFactory
            .getLogger(LoggerConstants.USER_ACTIVITY_LOGGER);

    /*
     * (non-Javadoc)
     * 
     * @see com.ovt.service.UserService#getUserByAccessToken(java.lang.String)
     */
    public User getUserByAccessToken(String accessToken)
            throws ServiceException
    {
        long userId = userAccessTokenManager.getUserByAccessToken(accessToken);

        logger.debug("Got userId {} by accessToken {}", userId, accessToken);

        User user = userManager.getUserById(userId);

        return user;
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.ovt.service.UserService#checkEmail(java.lang.String)
     */
    public boolean checkName(String name) throws ServiceException
    {

        checkNameUnique(name);

        return true;
    }

    /**
     * validate email and password format.
     * 
     * @throws InvalidDataInputException
     */
//    private void checkFormat(String email, String password)
//            throws ServiceException
//    {
//        checkNameFormat(email);
//
//        checkPasswordFormat(password);
//    }

    /**
     * check password format.
     * 
     * @param password
     * @throws InvalidDataInputException
     */
    private void checkPasswordFormat(String password)
            throws InvalidDataInputException
    {
        if (!DataFormatValidator.isValidPassword(password))
        {
            throw new InvalidDataInputException(
                    ServiceErrorCode.INVALID_DATA_FORMAT,
                    "Password format is invalid!");
        }
    }

    /**
     * check email format.
     * 
     * @param email
     * @throws InvalidDataInputException
     */
    private void checkNameFormat(String email)
            throws InvalidDataInputException
    {
        if (!DataFormatValidator.isValidEmail(email))
        {
            throw new InvalidDataInputException(
                    ServiceErrorCode.INVALID_DATA_FORMAT,
                    "Email format is invalid!");
        }
    }

    /**
     * check if email is unique.
     * 
     * @param email
     * @return
     * @throws ServiceException
     */
    private boolean checkNameUnique(String name) throws ServiceException
    {
        return userManager.checkName(name);
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.ovt.service.UserService#login(java.lang.String,
     * java.lang.String)
     */
    public String login(String name, String password) throws ServiceException
    {
        // check args' format
        //checkFormat(name, password);

        User user = userManager.getUserByName(name);
        if (user == null)
        {
            throw new ServiceException(ServiceErrorCode.USER_NOT_EXIST,
                    "User is not exist!");
        }

        // check password
        if (!StringUtils.equals(user.getPassword(),
                EncryptionUtils.encrypt(password)))
        {
            throw new ServiceException(ServiceErrorCode.WRONG_PASSWORD,
                    "Password is wrong!");
        }

        // generate access token
        String accessToken =
                userAccessTokenManager.generateAccessToken(user.getId());

        // put user into session context
        SessionContext.setCurrentUser(user);

        return accessToken;
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.ovt.service.UserService#logout(java.lang.String)
     */
    public void logout(String accessToken) throws ServiceException
    {
        if (StringUtils.isNotBlank(accessToken))
        {
            userAccessTokenManager.invalidToken(accessToken);
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.ovt.service.UserService#changePassword(java.lang.String,
     * java.lang.String)
     */
    public String changePassword(String oldPassword, String newPassword)
            throws ServiceException
    {
        // check format
        if (!DataFormatValidator.isValidPassword(oldPassword)
                || !DataFormatValidator.isValidPassword(newPassword))
        {
            throw new InvalidDataInputException(
                    ServiceErrorCode.INVALID_DATA_FORMAT,
                    "Password format is invalid!");
        }

        // new password can't be same as old one
        if (StringUtils.equals(oldPassword, newPassword))
        {
            throw new InvalidDataInputException(
                    ServiceErrorCode.WRONG_PASSWORD,
                    "New password can't be same as old one!");
        }

        User currentUser = SessionContext.getCurrentUser();

        // check old password
        if (!StringUtils.equals(currentUser.getPassword(),
                EncryptionUtils.encrypt(oldPassword)))
        {
            throw new InvalidDataInputException(
                    ServiceErrorCode.WRONG_PASSWORD, "Old password is wrong!");
        }

        // change password flow
        String accessToken = changePassword(newPassword, currentUser.getId());

        return accessToken;
    }

    /**
     * change password flow.
     * 
     * @param newPassword
     * @param currentUser
     * @return
     * @throws ServiceException
     */
    private String changePassword(String newPassword, long currentUserId)
            throws ServiceException
    {
        // update
        userManager.changePassword(currentUserId, newPassword);

        // invalid all old access tokens of user
        userAccessTokenManager.invalidTokensByUser(currentUserId);

        // generate new access token
        String accessToken =
                userAccessTokenManager.generateAccessToken(currentUserId);
        return accessToken;
    }

    

    /*
     * (non-Javadoc)
     * 
     * @see com.ovt.service.UserService#getUserList(com.ovt.dao.vo.PageInfo)
     */
    public List<User> getUserList(PageInfo pageInfo) throws ServiceException
    {
        return userManager.getUserList(pageInfo);
    }

    /**
     * create a new user.
     * 
     * @param email
     * @param password
     * @return
     * @throws ServiceException
     * @throws InvalidDataInputException
     */
    public User createUser(String name, String password) throws ServiceException
    {
        // do check
        checkName(name);
        checkPasswordFormat(password);

        // create new user
        return userManager.createUser(name, password);
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.ovt.service.UserService#deleteUser(java.lang.String)
     */
    @Override
    public void deleteUser(String name) throws ServiceException
    {
        // just need check email format
        checkNameFormat(name);

        userManager.deleteUser(name);
    }

    @Override
    public List<User> getUserByPermissionId(int permId) throws ServiceException
    {
        return userManager.getUserByPermission(permId);
    }

   
}
